---
title: 'Platforms'
---



## Intro

Bazel can build and test code on a variety of hardware, operating systems, and
system configurations. This can involve different versions of build tools like
linkers and compilers. To help manage this complexity, Bazel has the concepts
of *constraints* and *platforms*.

A *constraint* is a distinguishing property of a build or production machine.
Common constraints are CPU architecture, presence or absence of a GPU, or
version of a locally installed compiler. But constraints can be *anything* that
meaningfully distinguishes machines when orchestrating build work.

A *platform* is a collection of constraints that specifies a complete machine.
Bazel uses this concept to let developers choose which machines they want to
build for, which machines should run compile and test actions, and which
[toolchains](/extending/toolchains) build actions should compile with.

Developers can also use constraints to [select](/docs/configurable-attributes)
custom properties or dependencies on their build rules. For example: "*use
`src_arm.cc` when the build targets an Arm machine*".

## Platform types

Bazel recognizes three roles a platform may play:

*  **Host** - The platform on which Bazel itself runs.
*  **Execution** - A platform which runs compile actions to produce build outputs.
*  **Target** - A platform the code being built should run on.

Note: Builds only have one host platform, but often have multiple execution and
target platforms. For example, both remote Linux CI machines and developer Macs
may run build actions. And mobile apps have code for multiple phone types and
hardware extensions.

Builds generally have three kinds of relationships to platforms:

*  **Single-platform builds** - Host, execution, and target platforms are the
   same. For example, building on a developer machine without remote execution,
   then running the built binary on the same machine.

*  **Cross-compilation builds** - Host and execution platforms are the same,
   but the target platform is different. For example, building an iOS app on a
   Macbook Pro without remote execution.

*  **Multi-platform builds** - Host, execution, and target platforms are all
   different. For example, building an iOS app on a Macbook Pro and using remote
   Linux machines to compile C++ actions that don't need Xcode.

## Specifying platforms

The most common way for developers to use platforms is to specify desired
target machines with the `--platforms` flag:

```shell
$ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86
```

Organizations generally maintain their own platform definitions because build
machine setups vary between organizations.

When `--platforms` isn't set, it defaults to `@platforms//host`. This is
specially defined to auto-detect the host machine's OS and CPU properties so
builds target the same machine Bazel runs on. Build rules can
[select](/docs/configurable-attributes) on these properties with the
[@platforms/os](https://github.com/bazelbuild/platforms/blob/main/os/BUILD) and
[@platforms/cpu](https://github.com/bazelbuild/platforms/blob/main/cpu/BUILD)
constraints.

## Generally useful constraints and platforms {:#useful-constraints-platforms}

To keep the ecosystem consistent, Bazel team maintains a repository with
constraint definitions for the most popular CPU architectures and operating
systems. These are all defined in
[https://github.com/bazelbuild/platforms](https://github.com/bazelbuild/platforms){: .external}.

Bazel ships with the following special platform definition:
`@platforms//host` (aliased as `@bazel_tools//tools:host_platform`). This
auto-detects the OS and CPU properties of the machine Bazel runs on.

## Defining constraints {:#constraints}

Constraints are modeled with the [`constraint_setting`][constraint_setting] and
[`constraint_value`][constraint_value] build rules.

[constraint_setting]: /reference/be/platforms-and-toolchains#constraint_setting
[constraint_value]: /reference/be/platforms-and-toolchains#constraint_value

`constraint_setting` declares a type of property. For example:

```python
constraint_setting(name = "cpu")
```

`constraint_value` declares a possible value for that property:

```python
constraint_value(
    name = "x86",
    constraint_setting = ":cpu"
)
```

These can be referenced as labels when defining platforms or customizing build rules
on them. If the above examples are defined in `cpus/BUILD`, you can reference
the `x86` constraint as `//cpus:x86`.

If visibility allows, you can extend an existing `constraint_setting` by
defining your own value for it.

## Defining platforms {:#platforms}

The [`platform`](/reference/be/platforms-and-toolchains#platform) build rule
defines a platform as a collection of `constraint_value`s:

```python
platform(
    name = "linux_x86",
    constraint_values = [
        "//oses:linux",
        "//cpus:x86",
    ],
)
```

This models a machine that must have both the `//oses:linux` and `//cpus:x86`
constraints.

Platforms may only have one `constraint_value` for a given `constraint_setting`.
This means, for example, a platform can't have two CPUs unless you create another
`constraint_setting` type to model the second value.

## Skipping incompatible targets {:#skipping-incompatible-targets}

When building for a specific target platform it is often desirable to skip
targets that will never work on that platform. For example, your Windows device
driver is likely going to generate lots of compiler errors when building on a
Linux machine with `//...`. Use the
[`target_compatible_with`](/reference/be/common-definitions#common.target_compatible_with)
attribute to tell Bazel what target platform constraints your code has.

The simplest use of this attribute restricts a target to a single platform.
The target won't be built for any platform that doesn't satisfy all of the
constraints. The following example restricts `win_driver_lib.cc` to 64-bit
Windows.

```python
cc_library(
    name = "win_driver_lib",
    srcs = ["win_driver_lib.cc"],
    target_compatible_with = [
        "@platforms//cpu:x86_64",
        "@platforms//os:windows",
    ],
)
```

`:win_driver_lib` is *only* compatible for building with 64-bit Windows and
incompatible with all else. Incompatibility is transitive. Any targets
that transitively depend on an incompatible target are themselves considered
incompatible.

### When are targets skipped? {:#when-targets-skipped}

Targets are skipped when they are considered incompatible and included in the
build as part of a target pattern expansion. For example, the following two
invocations skip any incompatible targets found in a target pattern expansion.

```console
$ bazel build --platforms=//:myplatform //...
```

```console
$ bazel build --platforms=//:myplatform //:all
```

Incompatible tests in a [`test_suite`](/reference/be/general#test_suite) are
similarly skipped if the `test_suite` is specified on the command line with
[`--expand_test_suites`](/reference/command-line-reference#flag--expand_test_suites).
In other words, `test_suite` targets on the command line behave like `:all` and
`...`. Using `--noexpand_test_suites` prevents expansion and causes
`test_suite` targets with incompatible tests to also be incompatible.

Explicitly specifying an incompatible target on the command line results in an
error message and a failed build.

```console
$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully
```

Incompatible explicit targets are silently skipped if
`--skip_incompatible_explicit_targets` is enabled.

### More expressive constraints {:#expressive-constraints}

For more flexibility in expressing constraints, use the
`@platforms//:incompatible`
[`constraint_value`](/reference/be/platforms-and-toolchains#constraint_value)
that no platform satisfies.

Use [`select()`](/reference/be/functions#select) in combination with
`@platforms//:incompatible` to express more complicated restrictions. For
example, use it to implement basic OR logic. The following marks a library
compatible with macOS and Linux, but no other platforms.

Note: An empty constraints list is equivalent to "compatible with everything".

```python
cc_library(
    name = "unixish_lib",
    srcs = ["unixish_lib.cc"],
    target_compatible_with = select({
        "@platforms//os:osx": [],
        "@platforms//os:linux": [],
        "//conditions:default": ["@platforms//:incompatible"],
    }),
)
```

The above can be interpreted as follows:

1. When targeting macOS, the target has no constraints.
2. When targeting Linux, the target has no constraints.
3. Otherwise, the target has the `@platforms//:incompatible` constraint. Because
   `@platforms//:incompatible` is not part of any platform, the target is
   deemed incompatible.

To make your constraints more readable, use
[skylib](https://github.com/bazelbuild/bazel-skylib){: .external}'s
[`selects.with_or()`](https://github.com/bazelbuild/bazel-skylib/blob/main/docs/selects_doc.md#selectswith_or){: .external}.

You can express inverse compatibility in a similar way. The following example
describes a library that is compatible with everything _except_ for ARM.

```python
cc_library(
    name = "non_arm_lib",
    srcs = ["non_arm_lib.cc"],
    target_compatible_with = select({
        "@platforms//cpu:arm": ["@platforms//:incompatible"],
        "//conditions:default": [],
    }),
)
```

### Detecting incompatible targets using `bazel cquery` {:#cquery-incompatible-target-detection}

You can use the
[`IncompatiblePlatformProvider`](/rules/lib/providers/IncompatiblePlatformProvider)
in `bazel cquery`'s [Starlark output
format](/query/cquery#output-format-definition) to distinguish
incompatible targets from compatible ones.

This can be used to filter out incompatible targets. The example below will
only print the labels for targets that are compatible. Incompatible targets are
not printed.

```console
$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery
```

### Known Issues

Incompatible targets [ignore visibility
restrictions](https://github.com/bazelbuild/bazel/issues/16044).
